/*
 * Copyright (c) 2010, okazoh_tk. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *  - Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 *  - Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *  - Neither the name of the copyright holder nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#include "alkes/gfxfilter/GfxFilterGraphics.h"
#include "alkes/gfxfilter/GfxFilterRenderTarget.h"
#include "alkes/gfxfilter/GfxFilterVertexBuffer.h"

namespace alkes {

#define BOOL_STR(exp)   ((exp) ? "true" : "false")
#define IS_ENABLE_FILTER(classname, method) \
    static_cast<GfxFilterGraphics*>(getIGraphics3D())->isEnableLogFilter(AL_FILTER_##classname##method)


GfxFilterRenderTarget* GfxFilterRenderTarget::createRenderTarget2D(IGraphics3D* owner, IRenderTarget2D* target)
{
    if (!owner || !target) return 0;

    return new GfxFilterRenderTarget(owner, target);
}

GfxFilterRenderTarget* GfxFilterRenderTarget::createRenderTarget3D(IGraphics3D* owner, IRenderTarget3D* target)
{
    if (!owner || !target) return 0;

    return new GfxFilterRenderTarget(owner, target);
}

GfxFilterRenderTarget::GfxFilterRenderTarget(IGraphics3D* owner, IRenderTarget3D* target)
: IRenderTarget3D(owner, target->getTargetBehavior())
, is_3d_(true)
, component_(target)
{
}

GfxFilterRenderTarget::GfxFilterRenderTarget(IGraphics3D* owner, IRenderTarget2D* target)
: IRenderTarget3D(owner, target->getTargetBehavior())
, is_3d_(false)
, component_(target)
{
}

GfxFilterRenderTarget::~GfxFilterRenderTarget()
{
}

void GfxFilterRenderTarget::destroy()
{
    delete this;
}

void GfxFilterRenderTarget::onSnapshot(ImageWriter& writer, const Rect& rect)
{
    component_->snapshot(writer, &rect);
}

IRenderTarget2D* GfxFilterRenderTarget::get2D() const
{
    return component_;
}

IRenderTarget3D* GfxFilterRenderTarget::get3D() const
{
    if (is_3d_)
    {
        IRenderTarget2D* target = component_;
        return static_cast<IRenderTarget3D*>(target);
    }
    return 0;
}

void GfxFilterRenderTarget::beginDraw2D()
{
    if (IS_ENABLE_FILTER(IRenderTarget2D, beginDraw2D))
    {
        AL_LOG_INVOKED(IRenderTarget2D, beginDraw2D, "");
    }
    component_->beginDraw2D();
}

void GfxFilterRenderTarget::endDraw2D()
{
    if (IS_ENABLE_FILTER(IRenderTarget2D, endDraw2D))
    {
        AL_LOG_INVOKED(IRenderTarget2D, endDraw2D, "");
    }
    component_->endDraw2D();
}

const Rect GfxFilterRenderTarget::getTargetRect() const
{
    return component_->getTargetRect();
}

void GfxFilterRenderTarget::setScissorRect(const Rect& rect)
{
    if (IS_ENABLE_FILTER(IRenderTarget2D, setScissorRect))
    {
        AL_LOG_INVOKED(IRenderTarget2D, setScissorRect, "Rect(%d,%d,%d,%d)", rect.x, rect.y, rect.width, rect.height);
    }
    component_->setScissorRect(rect);
}

const Rect GfxFilterRenderTarget::getScissorRect()
{
    return component_->getScissorRect();
}

void GfxFilterRenderTarget::setState(int state, bool enable)
{
    if (IS_ENABLE_FILTER(IRenderTarget2D, setState))
    {
        AL_LOG_INVOKED(IRenderTarget2D, setState, "%d,%s", state, BOOL_STR(enable));
    }
    component_->setState(state, enable);
}

bool GfxFilterRenderTarget::getState(int state)
{
    return component_->getState(state);
}

void GfxFilterRenderTarget::setAlphaBlendMode(AlphaBlendMode src_blend, AlphaBlendMode dst_blend)
{
    if (IS_ENABLE_FILTER(IRenderTarget2D, setAlphaBlendMode))
    {
        AL_LOG_INVOKED(IRenderTarget2D, setAlphaBlendMode, "(AlphaBlendMode)%d, (AlphaBlendMode)%d", src_blend, dst_blend);
    }
    component_->setAlphaBlendMode(src_blend, dst_blend);
}

void GfxFilterRenderTarget::getAlphaBlendMode(AlphaBlendMode* src_blend, AlphaBlendMode* dst_blend)
{
    component_->getAlphaBlendMode(src_blend, dst_blend);
}

void GfxFilterRenderTarget::setTextureFilter(TexFilterValue min_filter, TexFilterValue mag_filter)
{
    if (IS_ENABLE_FILTER(IRenderTarget2D, setTextureFilter))
    {
        AL_LOG_INVOKED(IRenderTarget2D, setTextureFilter, "(TexFilterValue)%d, (TexFilterValue)%d", min_filter, mag_filter);
    }
    component_->setTextureFilter(min_filter, mag_filter);
}

void GfxFilterRenderTarget::getTextureFilter(TexFilterValue* min_filter, TexFilterValue* mag_filter)
{
    component_->getTextureFilter(min_filter, mag_filter);
}

void GfxFilterRenderTarget::setTextureAddressMode(TexAddressMode u_mode, TexAddressMode v_mode)
{
    if (IS_ENABLE_FILTER(IRenderTarget2D, setTextureAddressMode))
    {
        AL_LOG_INVOKED(IRenderTarget2D, setTextureAddressMode, "(TexAddressMode)%d, (TexAddressMode)%d", u_mode, v_mode);
    }
    component_->setTextureAddressMode(u_mode, v_mode);
}

void GfxFilterRenderTarget::getTextureAddressMode(TexAddressMode* u_mode, TexAddressMode* v_mode)
{
    component_->getTextureAddressMode(u_mode, v_mode);
}

bool GfxFilterRenderTarget::fill(const Color& color, const Rect* rect)
{
    if (IS_ENABLE_FILTER(IRenderTarget2D, fill))
    {
        if (rect)
        {
            AL_LOG_BEGIN_PARAM(Rect, false);
            AL_LOG_PARAM("%d,%d,%d,%d", rect->x, rect->y, rect->width, rect->height);
            AL_LOG_END_PARAM();
        }

        AL_LOG_INVOKED(IRenderTarget2D, fill, "Color(%d,%d,%d,%d), (const Rect*)temporary",
            color.r, color.g, color.b, color.a);
    }
    return component_->fill(color, rect);
}

bool GfxFilterRenderTarget::drawShape(Shape* shape, uint32_t start, uint32_t count)
{
    if (IS_ENABLE_FILTER(IRenderTarget2D, drawShape))
    {
        AL_LOG_INVOKED(IRenderTarget2D, drawShape, "0x%p, %d, %d",
            shape, start, count);
    }
    return component_->drawShape(shape, start, count);
}

bool GfxFilterRenderTarget::drawShape(LineShape* line_shape, uint32_t start, uint32_t count)
{
    if (IS_ENABLE_FILTER(IRenderTarget2D, drawShape))
    {
        AL_LOG_INVOKED(IRenderTarget2D, drawShape, "(LineShape*)0x%p, %d, %d",
            line_shape, start, count);
    }
    return component_->drawShape(line_shape, start, count);
}

bool GfxFilterRenderTarget::drawShape(RectangleShape* rectangle_shape, uint32_t start, uint32_t count)
{
    if (IS_ENABLE_FILTER(IRenderTarget2D, drawShape))
    {
        AL_LOG_INVOKED(IRenderTarget2D, drawShape, "(RectangleShape*)0x%p, %d, %d",
            rectangle_shape, start, count);
    }
    return component_->drawShape(rectangle_shape, start, count);
}

bool GfxFilterRenderTarget::drawShape(ImageShape* image_shape, uint32_t start, uint32_t count)
{
    if (IS_ENABLE_FILTER(IRenderTarget2D, drawShape))
    {
        AL_LOG_INVOKED(IRenderTarget2D, drawShape, "(ImageShape*)0x%p, %d, %d",
            image_shape, start, count);
    }
    return component_->drawShape(image_shape, start, count);
}

bool GfxFilterRenderTarget::drawShape(PolylineShape* polyline_shape, uint32_t start, uint32_t count)
{
    if (IS_ENABLE_FILTER(IRenderTarget2D, drawShape))
    {
        AL_LOG_INVOKED(IRenderTarget2D, drawShape, "(PolylineShape*)0x%p, %d, %d",
            polyline_shape, start, count);
    }
    return component_->drawShape(polyline_shape, start, count);
}

void GfxFilterRenderTarget::pushMatrix()
{
    if (IS_ENABLE_FILTER(IRenderTarget2D, pushMatrix))
    {
        AL_LOG_INVOKED(IRenderTarget2D, pushMatrix, "");
    }
    component_->pushMatrix();
}

void GfxFilterRenderTarget::popMatrix()
{
    if (IS_ENABLE_FILTER(IRenderTarget2D, popMatrix))
    {
        AL_LOG_INVOKED(IRenderTarget2D, popMatrix, "");
    }
    component_->popMatrix();
}

void GfxFilterRenderTarget::translate(const Vector2D& offset)
{
    if (IS_ENABLE_FILTER(IRenderTarget2D, translate))
    {
        AL_LOG_INVOKED(IRenderTarget2D, translate, "Vector2D(%f,%f)", offset.x, offset.y);
    }
    component_->translate(offset);
}

void GfxFilterRenderTarget::translate(float x, float y)
{
    if (IS_ENABLE_FILTER(IRenderTarget2D, translate))
    {
        AL_LOG_INVOKED(IRenderTarget2D, translate, "%f, %f", x, y);
    }
    component_->translate(x, y);
}

void GfxFilterRenderTarget::scale(float xscale, float yscale, const Vector2D& origin)
{
    if (IS_ENABLE_FILTER(IRenderTarget2D, scale))
    {
        AL_LOG_INVOKED(IRenderTarget2D, scale, "%f, %f, Vector2D(%f,%f)", xscale, yscale, origin.x, origin.y);
    }
    component_->scale(xscale, yscale, origin);
}

void GfxFilterRenderTarget::scale(float scaling, const Vector2D& origin)
{
    if (IS_ENABLE_FILTER(IRenderTarget2D, scale))
    {
        AL_LOG_INVOKED(IRenderTarget2D, scale, "%f, Vector2D(%f,%f)", scaling, origin.x, origin.y);
    }
    component_->scale(scaling, origin);
}

void GfxFilterRenderTarget::rotate(float angle, const Vector2D& origin)
{
    if (IS_ENABLE_FILTER(IRenderTarget2D, rotate))
    {
        AL_LOG_INVOKED(IRenderTarget2D, rotate, "%f, Vector2D(%f,%f)", angle, origin.x, origin.y);
    }
    component_->rotate(angle, origin);
}

void GfxFilterRenderTarget::skewX(float angle)
{
    if (IS_ENABLE_FILTER(IRenderTarget2D, skewX))
    {
        AL_LOG_INVOKED(IRenderTarget2D, skewX, "%f", angle);
    }
    component_->skewX(angle);
}

void GfxFilterRenderTarget::skewY(float angle)
{
    if (IS_ENABLE_FILTER(IRenderTarget2D, skewY))
    {
        AL_LOG_INVOKED(IRenderTarget2D, skewY, "%f", angle);
    }
    component_->skewY(angle);
}

void GfxFilterRenderTarget::beginDraw3D()
{
    if (IS_ENABLE_FILTER(IRenderTarget3D, beginDraw3D))
    {
        AL_LOG_INVOKED(IRenderTarget3D, beginDraw3D, "");
    }

    IRenderTarget3D* target = get3D();
    if (!target)
    {
        return ;
    }

    target->beginDraw3D();
}

void GfxFilterRenderTarget::endDraw3D()
{
    if (IS_ENABLE_FILTER(IRenderTarget3D, endDraw3D))
    {
        AL_LOG_INVOKED(IRenderTarget3D, endDraw3D, "");
    }

    IRenderTarget3D* target = get3D();
    if (!target)
    {
        return ;
    }
    target->endDraw3D();
}

void GfxFilterRenderTarget::setViewport(const Viewport& viewport)
{
    if (IS_ENABLE_FILTER(IRenderTarget3D, setViewport))
    {
        AL_LOG_INVOKED(IRenderTarget3D, setViewport, "Viewport(%d,%d,%d,%d)", viewport.x, viewport.y, viewport.w, viewport.h);
    }

    IRenderTarget3D* target = get3D();
    if (!target)
    {
        return ;
    }
    target->setViewport(viewport);
}

const Viewport GfxFilterRenderTarget::getViewport() const
{
    const IRenderTarget3D* target = get3D();
    if (!target)
    {
        Viewport v = {0,0,0,0};
        return v;
    }
    return target->getViewport();
}

void GfxFilterRenderTarget::setPerspective(float angle, float aspect, float znear, float zfar)
{
    if (IS_ENABLE_FILTER(IRenderTarget3D, setPerspective))
    {
        AL_LOG_INVOKED(IRenderTarget3D, setPerspective, "Perspective(%f,%f,%f,%f)", angle, aspect, znear, zfar);
    }

    IRenderTarget3D* target = get3D();
    if (!target)
    {
        return ;
    }
    target->setPerspective(angle, aspect, znear, zfar);
}

void GfxFilterRenderTarget::setLookAt(const Vector3D& eye, const Vector3D& at, const Vector3D& up)
{
    if (IS_ENABLE_FILTER(IRenderTarget3D, setLookAt))
    {
        AL_LOG_INVOKED(IRenderTarget3D, setLookAt, "LookAt()");
    }

    IRenderTarget3D* target = get3D();
    if (!target)
    {
        return ;
    }
    target->setLookAt(eye, at, up);
}

void GfxFilterRenderTarget::translate(const Vector3D& offset)
{
    if (IS_ENABLE_FILTER(IRenderTarget3D, translate))
    {
        AL_LOG_INVOKED(IRenderTarget3D, translate, "translate(%f,%f,%f)", offset.x, offset.y, offset.z);
    }

    IRenderTarget3D* target = get3D();
    if (!target)
    {
        return ;
    }
    target->translate(offset);
}

void GfxFilterRenderTarget::translate(float x, float y, float z)
{
    if (IS_ENABLE_FILTER(IRenderTarget3D, translate))
    {
        AL_LOG_INVOKED(IRenderTarget3D, translate, "translate(%f,%f,%f)", x, y, z);
    }

    IRenderTarget3D* target = get3D();
    if (!target)
    {
        return ;
    }
    target->translate(x, y, z);
}

void GfxFilterRenderTarget::scale(float xscale, float yscale, float zscale)
{
    if (IS_ENABLE_FILTER(IRenderTarget3D, scale))
    {
        AL_LOG_INVOKED(IRenderTarget3D, scale, "scale(%f,%f,%f)", xscale, yscale, zscale);
    }

    IRenderTarget3D* target = get3D();
    if (!target)
    {
        return ;
    }
    target->scale(xscale, yscale, zscale);
}

void GfxFilterRenderTarget::rotate(float angle, float x, float y, float z)
{
    if (IS_ENABLE_FILTER(IRenderTarget3D, rotate))
    {
        AL_LOG_INVOKED(IRenderTarget3D, rotate, "rotate(%f,%f,%f,%f)", angle, x, y, z);
    }

    IRenderTarget3D* target = get3D();
    if (!target)
    {
        return ;
    }
    target->rotate(angle, x, y, z);
}

void GfxFilterRenderTarget::setTextureFilter(TexFilterValue min_filter, TexFilterValue mag_filter, int stage)
{
    if (IS_ENABLE_FILTER(IRenderTarget3D, setTextureFilter))
    {
        AL_LOG_INVOKED(IRenderTarget3D, setTextureFilter, "(TexFilterValue)%d, (TexFilterValue)%d, %d",
            min_filter, mag_filter, stage);
    }

    IRenderTarget3D* target = get3D();
    if (!target)
    {
        return ;
    }
    target->setTextureFilter(min_filter, mag_filter, stage);
}

void GfxFilterRenderTarget::getTextureFilter(TexFilterValue* min_filter, TexFilterValue* mag_filter, int stage)
{
    IRenderTarget3D* target = get3D();
    if (!target)
    {
        return ;
    }
    return target->getTextureFilter(min_filter, mag_filter, stage);
}

void GfxFilterRenderTarget::setTextureAddressMode(TexAddressMode u_mode, TexAddressMode v_mode, int stage)
{
    if (IS_ENABLE_FILTER(IRenderTarget3D, setTextureAddressMode))
    {
        AL_LOG_INVOKED(IRenderTarget3D, setTextureAddressMode, "(TexAddressMode)%d, (TexAddressMode)%d, %d",
            u_mode, v_mode, stage);
    }

    IRenderTarget3D* target = get3D();
    if (!target)
    {
        return ;
    }
    target->setTextureAddressMode(u_mode, v_mode, stage);
}

void GfxFilterRenderTarget::getTextureAddressMode(TexAddressMode* u_mode, TexAddressMode* v_mode, int stage)
{
    IRenderTarget3D* target = get3D();
    if (!target)
    {
        return ;
    }
    target->getTextureAddressMode(u_mode, v_mode, stage);
}

void GfxFilterRenderTarget::clearBuffer(uint32_t buffer_flag, const Color& clear_color, const Rect* rect, float depth)
{
    if (IS_ENABLE_FILTER(IRenderTarget3D, clearBuffer))
    {
        if (rect)
        {
            AL_LOG_BEGIN_PARAM(Rect, false);
            AL_LOG_PARAM("%d,%d,%d,%d", rect->x, rect->y, rect->width, rect->height);
            AL_LOG_END_PARAM();
        }
        AL_LOG_INVOKED(IRenderTarget3D, clearBuffer, "0x%08x, Color(%d,%d,%d,%d), (Rect*)temporary, %f",
            clear_color.r, clear_color.g, clear_color.b, clear_color.a,
            buffer_flag, depth);
    }

    IRenderTarget3D* target = get3D();
    if (!target)
    {
        return ;
    }
    target->clearBuffer(buffer_flag, clear_color, rect, depth);
}

bool GfxFilterRenderTarget::drawPrimitive(IVertexBuffer* vbuffer, PrimitiveType type, uint32_t start, uint32_t count)
{
    if (IS_ENABLE_FILTER(IRenderTarget3D, drawPrimitive))
    {
        AL_LOG_INVOKED(IRenderTarget3D, drawPrimitive, "0x%p, (PrimitiveType)type, %d, %d",
            AL_REAL_PTR(vbuffer, GfxFilterVertexBuffer), type, start, count);
    }

    IRenderTarget3D* target = get3D();
    if (!target)
    {
        return false;
    }
    return target->drawPrimitive(vbuffer, type, start, count);
}

bool GfxFilterRenderTarget::setTexture(Image* image, int stage)
{
    if (IS_ENABLE_FILTER(IRenderTarget3D, setTexture))
    {
        if (image)
        {
            uint16_t x, y, w, h;
            image->getOffset(x, y);
            image->getSize(w, h);
            AL_LOG_INVOKED(IRenderTarget3D, setTexture, "Image(0x%p,%d,%d,%d,%d), %d",
                image->getImageBuffer(), x, y, w, h, stage);
        }
        else
        {
            AL_LOG_INVOKED(IRenderTarget3D, setTexture, "Image(0x00000000), %d",
                stage);
        }
    }

    IRenderTarget3D* target = get3D();
    if (!target)
    {
        return false;
    }
    return target->setTexture(image, stage);
}

}
